/*****************************************************************************
 * This file is part of Kvazaar HEVC encoder.
 *
 * Copyright (c) 2021, Tampere University, ITU/ISO/IEC, project contributors
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 * 
 * * Neither the name of the Tampere University or ITU/ISO/IEC nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
 ****************************************************************************/

#include "strategies/avx2/intra-avx2.h"
#if COMPILE_INTEL_AVX2 && defined X86_64
#include "kvazaar.h"
#if KVZ_BIT_DEPTH == 8

#include <immintrin.h>
#include <stdlib.h>

#include "strategyselector.h"
#include "strategies/missing-intel-intrinsics.h"


static const int16_t delta_int_table[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 13, 14, 15, 16, 17, 17, 18, 19, 20, 21, 21, 22, 23, 24, 25, 26,
0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19, 20, 21,
0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 17,
0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13,
0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -5, -5,
-1, -1, -1, -2, -2, -2, -2, -3, -3, -3, -4, -4, -4, -4, -5, -5, -5, -6, -6, -6, -6, -7, -7, -7, -8, -8, -8, -8, -9, -9, -9, -9,
-1, -1, -2, -2, -3, -3, -3, -4, -4, -5, -5, -5, -6, -6, -7, -7, -7, -8, -8, -9, -9, -9, -10, -10, -11, -11, -11, -12, -12, -13, -13, -13,
-1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8, -8, -9, -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -15, -15, -16, -16, -17, -17,
-1, -2, -2, -3, -4, -4, -5, -6, -6, -7, -8, -8, -9, -10, -10, -11, -12, -12, -13, -14, -14, -15, -16, -16, -17, -18, -18, -19, -20, -20, -21, -21,
-1, -2, -3, -4, -5, -5, -6, -7, -8, -9, -9, -10, -11, -12, -13, -13, -14, -15, -16, -17, -18, -18, -19, -20, -21, -22, -22, -23, -24, -25, -26, -26,
-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32,
};

static ALIGNED(32) const int8_t intra_chroma_linear_interpolation_weights_w8_ver[] = {
32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 2
 6, 26, 12, 20, 18, 14, 24,  8, 30,  2,  4, 28, 10, 22, 16, 16, 22, 10, 28,  4,  2, 30,  8, 24, 14, 18, 20, 12, 26,  6, 32,  0,  6, 26, 12, 20, 18, 14, 24,  8, 30,  2,  4, 28, 10, 22, 16, 16, 22, 10, 28,  4,  2, 30,  8, 24, 14, 18, 20, 12, 26,  6, 32,  0,   // Mode 3
11, 21, 22, 10,  1, 31, 12, 20, 23,  9,  2, 30, 13, 19, 24,  8,  3, 29, 14, 18, 25,  7,  4, 28, 15, 17, 26,  6,  5, 27, 16, 16, 27,  5,  6, 26, 17, 15, 28,  4,  7, 25, 18, 14, 29,  3,  8, 24, 19, 13, 30,  2,  9, 23, 20, 12, 31,  1, 10, 22, 21, 11, 32,  0,   // Mode 4
15, 17, 30,  2, 13, 19, 28,  4, 11, 21, 26,  6,  9, 23, 24,  8,  7, 25, 22, 10,  5, 27, 20, 12,  3, 29, 18, 14,  1, 31, 16, 16, 31,  1, 14, 18, 29,  3, 12, 20, 27,  5, 10, 22, 25,  7,  8, 24, 23,  9,  6, 26, 21, 11,  4, 28, 19, 13,  2, 30, 17, 15, 32,  0,   // Mode 5
19, 13,  6, 26, 25,  7, 12, 20, 31,  1, 18, 14,  5, 27, 24,  8, 11, 21, 30,  2, 17, 15,  4, 28, 23,  9, 10, 22, 29,  3, 16, 16,  3, 29, 22, 10,  9, 23, 28,  4, 15, 17,  2, 30, 21, 11,  8, 24, 27,  5, 14, 18,  1, 31, 20, 12,  7, 25, 26,  6, 13, 19, 32,  0,   // Mode 6
23,  9, 14, 18,  5, 27, 28,  4, 19, 13, 10, 22,  1, 31, 24,  8, 15, 17,  6, 26, 29,  3, 20, 12, 11, 21,  2, 30, 25,  7, 16, 16,  7, 25, 30,  2, 21, 11, 12, 20,  3, 29, 26,  6, 17, 15,  8, 24, 31,  1, 22, 10, 13, 19,  4, 28, 27,  5, 18, 14,  9, 23, 32,  0,   // Mode 7
27,  5, 22, 10, 17, 15, 12, 20,  7, 25,  2, 30, 29,  3, 24,  8, 19, 13, 14, 18,  9, 23,  4, 28, 31,  1, 26,  6, 21, 11, 16, 16, 11, 21,  6, 26,  1, 31, 28,  4, 23,  9, 18, 14, 13, 19,  8, 24,  3, 29, 30,  2, 25,  7, 20, 12, 15, 17, 10, 22,  5, 27, 32,  0,   // Mode 8
30,  2, 28,  4, 26,  6, 24,  8, 22, 10, 20, 12, 18, 14, 16, 16, 14, 18, 12, 20, 10, 22,  8, 24,  6, 26,  4, 28,  2, 30, 32,  0, 30,  2, 28,  4, 26,  6, 24,  8, 22, 10, 20, 12, 18, 14, 16, 16, 14, 18, 12, 20, 10, 22,  8, 24,  6, 26,  4, 28,  2, 30, 32,  0,   // Mode 9
32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 10
 2, 30,  4, 28,  6, 26,  8, 24, 10, 22, 12, 20, 14, 18, 16, 16, 18, 14, 20, 12, 22, 10, 24,  8, 26,  6, 28,  4, 30,  2, 32,  0,  2, 30,  4, 28,  6, 26,  8, 24, 10, 22, 12, 20, 14, 18, 16, 16, 18, 14, 20, 12, 22, 10, 24,  8, 26,  6, 28,  4, 30,  2, 32,  0,   // Mode 11
 5, 27, 10, 22, 15, 17, 20, 12, 25,  7, 30,  2,  3, 29,  8, 24, 13, 19, 18, 14, 23,  9, 28,  4,  1, 31,  6, 26, 11, 21, 16, 16, 21, 11, 26,  6, 31,  1,  4, 28,  9, 23, 14, 18, 19, 13, 24,  8, 29,  3,  2, 30,  7, 25, 12, 20, 17, 15, 22, 10, 27,  5, 32,  0,   // Mode 12
 9, 23, 18, 14, 27,  5,  4, 28, 13, 19, 22, 10, 31,  1,  8, 24, 17, 15, 26,  6,  3, 29, 12, 20, 21, 11, 30,  2,  7, 25, 16, 16, 25,  7,  2, 30, 11, 21, 20, 12, 29,  3,  6, 26, 15, 17, 24,  8,  1, 31, 10, 22, 19, 13, 28,  4,  5, 27, 14, 18, 23,  9, 32,  0,   // Mode 13
13, 19, 26,  6,  7, 25, 20, 12,  1, 31, 14, 18, 27,  5,  8, 24, 21, 11,  2, 30, 15, 17, 28,  4,  9, 23, 22, 10,  3, 29, 16, 16, 29,  3, 10, 22, 23,  9,  4, 28, 17, 15, 30,  2, 11, 21, 24,  8,  5, 27, 18, 14, 31,  1, 12, 20, 25,  7,  6, 26, 19, 13, 32,  0,   // Mode 14
17, 15,  2, 30, 19, 13,  4, 28, 21, 11,  6, 26, 23,  9,  8, 24, 25,  7, 10, 22, 27,  5, 12, 20, 29,  3, 14, 18, 31,  1, 16, 16,  1, 31, 18, 14,  3, 29, 20, 12,  5, 27, 22, 10,  7, 25, 24,  8,  9, 23, 26,  6, 11, 21, 28,  4, 13, 19, 30,  2, 15, 17, 32,  0,   // Mode 15
21, 11, 10, 22, 31,  1, 20, 12,  9, 23, 30,  2, 19, 13,  8, 24, 29,  3, 18, 14,  7, 25, 28,  4, 17, 15,  6, 26, 27,  5, 16, 16,  5, 27, 26,  6, 15, 17,  4, 28, 25,  7, 14, 18,  3, 29, 24,  8, 13, 19,  2, 30, 23,  9, 12, 20,  1, 31, 22, 10, 11, 21, 32,  0,   // Mode 16
26,  6, 20, 12, 14, 18,  8, 24,  2, 30, 28,  4, 22, 10, 16, 16, 10, 22,  4, 28, 30,  2, 24,  8, 18, 14, 12, 20,  6, 26, 32,  0, 26,  6, 20, 12, 14, 18,  8, 24,  2, 30, 28,  4, 22, 10, 16, 16, 10, 22,  4, 28, 30,  2, 24,  8, 18, 14, 12, 20,  6, 26, 32,  0,   // Mode 17
32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 18
};

static ALIGNED(32) const int8_t intra_chroma_linear_interpolation_weights_w8_hor[] = {
32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 2
 6, 26, 12, 20, 18, 14, 24,  8, 30,  2,  4, 28, 10, 22, 16, 16,   // Mode 3
11, 21, 22, 10,  1, 31, 12, 20, 23,  9,  2, 30, 13, 19, 24,  8,   // Mode 4
15, 17, 30,  2, 13, 19, 28,  4, 11, 21, 26,  6,  9, 23, 24,  8,   // Mode 5
19, 13,  6, 26, 25,  7, 12, 20, 31,  1, 18, 14,  5, 27, 24,  8,   // Mode 6
23,  9, 14, 18,  5, 27, 28,  4, 19, 13, 10, 22,  1, 31, 24,  8,   // Mode 7
27,  5, 22, 10, 17, 15, 12, 20,  7, 25,  2, 30, 29,  3, 24,  8,   // Mode 8
30,  2, 28,  4, 26,  6, 24,  8, 22, 10, 20, 12, 18, 14, 16, 16,   // Mode 9
32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 10
 2, 30,  4, 28,  6, 26,  8, 24, 10, 22, 12, 20, 14, 18, 16, 16,   // Mode 11
 5, 27, 10, 22, 15, 17, 20, 12, 25,  7, 30,  2,  3, 29,  8, 24,   // Mode 12
 9, 23, 18, 14, 27,  5,  4, 28, 13, 19, 22, 10, 31,  1,  8, 24,   // Mode 13
13, 19, 26,  6,  7, 25, 20, 12,  1, 31, 14, 18, 27,  5,  8, 24,   // Mode 14
17, 15,  2, 30, 19, 13,  4, 28, 21, 11,  6, 26, 23,  9,  8, 24,   // Mode 15
21, 11, 10, 22, 31,  1, 20, 12,  9, 23, 30,  2, 19, 13,  8, 24,   // Mode 16
26,  6, 20, 12, 14, 18,  8, 24,  2, 30, 28,  4, 22, 10, 16, 16,   // Mode 17
32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 18
};

static ALIGNED(32) const int8_t intra_chroma_linear_interpolation_shuffle_vectors_w8_hor[] = {
0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08 , // Mode 2
0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09,
0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07 , // Mode 3
0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08,
0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06 , // Mode 4
0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05 , // Mode 5
0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04 , // Mode 6
0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03 , // Mode 7
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02 , // Mode 8
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 , // Mode 9
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 , // Mode 10
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01 , // Mode 11
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01 , // Mode 12
0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02,
0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01 , // Mode 13
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02,
0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01 , // Mode 14
0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02,
0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01 , // Mode 15
0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02,
0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01 , // Mode 16
0x06, 0x07, 0x05, 0x06, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02,
0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01 , // Mode 17
0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02,
0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01 , // Mode 18
0x08, 0x09, 0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02,
};

static ALIGNED(32) const int8_t intra_chroma_linear_interpolation_weights_w16_hor[] = {
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 2
   6, 26, 12, 20, 18, 14, 24,  8, 30,  2,  4, 28, 10, 22, 16, 16, 22, 10, 28,  4,  2, 30,  8, 24, 14, 18, 20, 12, 26,  6, 32,  0,   // Mode 3
  11, 21, 22, 10,  1, 31, 12, 20, 23,  9,  2, 30, 13, 19, 24,  8,  3, 29, 14, 18, 25,  7,  4, 28, 15, 17, 26,  6,  5, 27, 16, 16,   // Mode 4
  15, 17, 30,  2, 13, 19, 28,  4, 11, 21, 26,  6,  9, 23, 24,  8,  7, 25, 22, 10,  5, 27, 20, 12,  3, 29, 18, 14,  1, 31, 16, 16,   // Mode 5
  19, 13,  6, 26, 25,  7, 12, 20, 31,  1, 18, 14,  5, 27, 24,  8, 11, 21, 30,  2, 17, 15,  4, 28, 23,  9, 10, 22, 29,  3, 16, 16,   // Mode 6
  23,  9, 14, 18,  5, 27, 28,  4, 19, 13, 10, 22,  1, 31, 24,  8, 15, 17,  6, 26, 29,  3, 20, 12, 11, 21,  2, 30, 25,  7, 16, 16,   // Mode 7
  27,  5, 22, 10, 17, 15, 12, 20,  7, 25,  2, 30, 29,  3, 24,  8, 19, 13, 14, 18,  9, 23,  4, 28, 31,  1, 26,  6, 21, 11, 16, 16,   // Mode 8
  30,  2, 28,  4, 26,  6, 24,  8, 22, 10, 20, 12, 18, 14, 16, 16, 14, 18, 12, 20, 10, 22,  8, 24,  6, 26,  4, 28,  2, 30, 32,  0,   // Mode 9
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 10
   2, 30,  4, 28,  6, 26,  8, 24, 10, 22, 12, 20, 14, 18, 16, 16, 18, 14, 20, 12, 22, 10, 24,  8, 26,  6, 28,  4, 30,  2, 32,  0,   // Mode 11
   5, 27, 10, 22, 15, 17, 20, 12, 25,  7, 30,  2,  3, 29,  8, 24, 13, 19, 18, 14, 23,  9, 28,  4,  1, 31,  6, 26, 11, 21, 16, 16,   // Mode 12
   9, 23, 18, 14, 27,  5,  4, 28, 13, 19, 22, 10, 31,  1,  8, 24, 17, 15, 26,  6,  3, 29, 12, 20, 21, 11, 30,  2,  7, 25, 16, 16,   // Mode 13
  13, 19, 26,  6,  7, 25, 20, 12,  1, 31, 14, 18, 27,  5,  8, 24, 21, 11,  2, 30, 15, 17, 28,  4,  9, 23, 22, 10,  3, 29, 16, 16,   // Mode 14
  17, 15,  2, 30, 19, 13,  4, 28, 21, 11,  6, 26, 23,  9,  8, 24, 25,  7, 10, 22, 27,  5, 12, 20, 29,  3, 14, 18, 31,  1, 16, 16,   // Mode 15
  21, 11, 10, 22, 31,  1, 20, 12,  9, 23, 30,  2, 19, 13,  8, 24, 29,  3, 18, 14,  7, 25, 28,  4, 17, 15,  6, 26, 27,  5, 16, 16,   // Mode 16
  26,  6, 20, 12, 14, 18,  8, 24,  2, 30, 28,  4, 22, 10, 16, 16, 10, 22,  4, 28, 30,  2, 24,  8, 18, 14, 12, 20,  6, 26, 32,  0,   // Mode 17
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 18
  };

static ALIGNED(32) const int8_t intra_chroma_linear_interpolation_shuffle_vectors_w16_hor[] = {
0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08,  // Mode 2
0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08,
0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09,
0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09,
0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,  // Mode 3
0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08,
0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08,
0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,  // Mode 4
0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07,
0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,  // Mode 5
0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04,  // Mode 6
0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04,
0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03,  // Mode 7
0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04,
0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02,  // Mode 8
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,  // Mode 9
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,  // Mode 10
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,  // Mode 11
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01,  // Mode 12
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02,
0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,  // Mode 13
0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01,
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02,
0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02,
0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,  // Mode 14
0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01,
0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02,
0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02,
0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,  // Mode 15
0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,
0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02,
0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02,
0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01,  // Mode 16
0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,
0x06, 0x07, 0x05, 0x06, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02,
0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02,
0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01,  // Mode 17
0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01,
0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02,
0x06, 0x07, 0x05, 0x06, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02,
0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01,  // Mode 18
0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01,
0x08, 0x09, 0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02,
0x08, 0x09, 0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, };


static ALIGNED(32) const int8_t intra_chroma_linear_interpolation_weights_w32_hor[] = {
32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 2
 6, 26, 12, 20, 18, 14, 24,  8, 30,  2,  4, 28, 10, 22, 16, 16, 22, 10, 28,  4,  2, 30,  8, 24, 14, 18, 20, 12, 26,  6, 32,  0,  6, 26, 12, 20, 18, 14, 24,  8, 30,  2,  4, 28, 10, 22, 16, 16, 22, 10, 28,  4,  2, 30,  8, 24, 14, 18, 20, 12, 26,  6, 32,  0,   // Mode 3
11, 21, 22, 10,  1, 31, 12, 20, 23,  9,  2, 30, 13, 19, 24,  8,  3, 29, 14, 18, 25,  7,  4, 28, 15, 17, 26,  6,  5, 27, 16, 16, 27,  5,  6, 26, 17, 15, 28,  4,  7, 25, 18, 14, 29,  3,  8, 24, 19, 13, 30,  2,  9, 23, 20, 12, 31,  1, 10, 22, 21, 11, 32,  0,   // Mode 4
15, 17, 30,  2, 13, 19, 28,  4, 11, 21, 26,  6,  9, 23, 24,  8,  7, 25, 22, 10,  5, 27, 20, 12,  3, 29, 18, 14,  1, 31, 16, 16, 31,  1, 14, 18, 29,  3, 12, 20, 27,  5, 10, 22, 25,  7,  8, 24, 23,  9,  6, 26, 21, 11,  4, 28, 19, 13,  2, 30, 17, 15, 32,  0,   // Mode 5
19, 13,  6, 26, 25,  7, 12, 20, 31,  1, 18, 14,  5, 27, 24,  8, 11, 21, 30,  2, 17, 15,  4, 28, 23,  9, 10, 22, 29,  3, 16, 16,  3, 29, 22, 10,  9, 23, 28,  4, 15, 17,  2, 30, 21, 11,  8, 24, 27,  5, 14, 18,  1, 31, 20, 12,  7, 25, 26,  6, 13, 19, 32,  0,   // Mode 6
23,  9, 14, 18,  5, 27, 28,  4, 19, 13, 10, 22,  1, 31, 24,  8, 15, 17,  6, 26, 29,  3, 20, 12, 11, 21,  2, 30, 25,  7, 16, 16,  7, 25, 30,  2, 21, 11, 12, 20,  3, 29, 26,  6, 17, 15,  8, 24, 31,  1, 22, 10, 13, 19,  4, 28, 27,  5, 18, 14,  9, 23, 32,  0,   // Mode 7
27,  5, 22, 10, 17, 15, 12, 20,  7, 25,  2, 30, 29,  3, 24,  8, 19, 13, 14, 18,  9, 23,  4, 28, 31,  1, 26,  6, 21, 11, 16, 16, 11, 21,  6, 26,  1, 31, 28,  4, 23,  9, 18, 14, 13, 19,  8, 24,  3, 29, 30,  2, 25,  7, 20, 12, 15, 17, 10, 22,  5, 27, 32,  0,   // Mode 8
30,  2, 28,  4, 26,  6, 24,  8, 22, 10, 20, 12, 18, 14, 16, 16, 14, 18, 12, 20, 10, 22,  8, 24,  6, 26,  4, 28,  2, 30, 32,  0, 30,  2, 28,  4, 26,  6, 24,  8, 22, 10, 20, 12, 18, 14, 16, 16, 14, 18, 12, 20, 10, 22,  8, 24,  6, 26,  4, 28,  2, 30, 32,  0,   // Mode 9
32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 10
 2, 30,  4, 28,  6, 26,  8, 24, 10, 22, 12, 20, 14, 18, 16, 16, 18, 14, 20, 12, 22, 10, 24,  8, 26,  6, 28,  4, 30,  2, 32,  0,  2, 30,  4, 28,  6, 26,  8, 24, 10, 22, 12, 20, 14, 18, 16, 16, 18, 14, 20, 12, 22, 10, 24,  8, 26,  6, 28,  4, 30,  2, 32,  0,   // Mode 11
 5, 27, 10, 22, 15, 17, 20, 12, 25,  7, 30,  2,  3, 29,  8, 24, 13, 19, 18, 14, 23,  9, 28,  4,  1, 31,  6, 26, 11, 21, 16, 16, 21, 11, 26,  6, 31,  1,  4, 28,  9, 23, 14, 18, 19, 13, 24,  8, 29,  3,  2, 30,  7, 25, 12, 20, 17, 15, 22, 10, 27,  5, 32,  0,   // Mode 12
 9, 23, 18, 14, 27,  5,  4, 28, 13, 19, 22, 10, 31,  1,  8, 24, 17, 15, 26,  6,  3, 29, 12, 20, 21, 11, 30,  2,  7, 25, 16, 16, 25,  7,  2, 30, 11, 21, 20, 12, 29,  3,  6, 26, 15, 17, 24,  8,  1, 31, 10, 22, 19, 13, 28,  4,  5, 27, 14, 18, 23,  9, 32,  0,   // Mode 13
13, 19, 26,  6,  7, 25, 20, 12,  1, 31, 14, 18, 27,  5,  8, 24, 21, 11,  2, 30, 15, 17, 28,  4,  9, 23, 22, 10,  3, 29, 16, 16, 29,  3, 10, 22, 23,  9,  4, 28, 17, 15, 30,  2, 11, 21, 24,  8,  5, 27, 18, 14, 31,  1, 12, 20, 25,  7,  6, 26, 19, 13, 32,  0,   // Mode 14
17, 15,  2, 30, 19, 13,  4, 28, 21, 11,  6, 26, 23,  9,  8, 24, 25,  7, 10, 22, 27,  5, 12, 20, 29,  3, 14, 18, 31,  1, 16, 16,  1, 31, 18, 14,  3, 29, 20, 12,  5, 27, 22, 10,  7, 25, 24,  8,  9, 23, 26,  6, 11, 21, 28,  4, 13, 19, 30,  2, 15, 17, 32,  0,   // Mode 15
21, 11, 10, 22, 31,  1, 20, 12,  9, 23, 30,  2, 19, 13,  8, 24, 29,  3, 18, 14,  7, 25, 28,  4, 17, 15,  6, 26, 27,  5, 16, 16,  5, 27, 26,  6, 15, 17,  4, 28, 25,  7, 14, 18,  3, 29, 24,  8, 13, 19,  2, 30, 23,  9, 12, 20,  1, 31, 22, 10, 11, 21, 32,  0,   // Mode 16
26,  6, 20, 12, 14, 18,  8, 24,  2, 30, 28,  4, 22, 10, 16, 16, 10, 22,  4, 28, 30,  2, 24,  8, 18, 14, 12, 20,  6, 26, 32,  0, 26,  6, 20, 12, 14, 18,  8, 24,  2, 30, 28,  4, 22, 10, 16, 16, 10, 22,  4, 28, 30,  2, 24,  8, 18, 14, 12, 20,  6, 26, 32,  0,   // Mode 17
32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 18
};

static ALIGNED(32) const int8_t intra_chroma_linear_interpolation_shuffle_vectors_w32_hor[] = {
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10,  // Mode 2
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e,  // Mode 3
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e,
  0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0a, 0x09, 0x0a, 0x0a, 0x0b,  // Mode 4
  0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b,
  0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, 0x06, 0x07, 0x07, 0x08, 0x07, 0x08, 0x08, 0x09,  // Mode 5
  0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09,
  0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, 0x06, 0x07,  // Mode 6
  0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, 0x06, 0x07, 0x07, 0x08,
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05,  // Mode 7
  0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03,  // Mode 8
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02,  // Mode 9
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02,
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,  // Mode 10
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,  // Mode 11
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,  // Mode 12
  0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01,  // Mode 13
  0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x06, 0x07, 0x06, 0x07, 0x05, 0x06, 0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01,  // Mode 14
  0x06, 0x07, 0x05, 0x06, 0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
  0x08, 0x09, 0x07, 0x08, 0x07, 0x08, 0x06, 0x07, 0x06, 0x07, 0x05, 0x06, 0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,  // Mode 15
  0x07, 0x08, 0x07, 0x08, 0x06, 0x07, 0x06, 0x07, 0x05, 0x06, 0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01,
  0x0a, 0x0b, 0x09, 0x0a, 0x09, 0x0a, 0x08, 0x09, 0x07, 0x08, 0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,  // Mode 16
  0x09, 0x0a, 0x09, 0x0a, 0x08, 0x09, 0x07, 0x08, 0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01,
  0x0c, 0x0d, 0x0b, 0x0c, 0x0a, 0x0b, 0x09, 0x0a, 0x08, 0x09, 0x08, 0x09, 0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01,  // Mode 17
  0x0c, 0x0d, 0x0b, 0x0c, 0x0a, 0x0b, 0x09, 0x0a, 0x08, 0x09, 0x08, 0x09, 0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01,
  0x0f, 0x10, 0x0e, 0x0f, 0x0d, 0x0e, 0x0c, 0x0d, 0x0b, 0x0c, 0x0a, 0x0b, 0x09, 0x0a, 0x08, 0x09, 0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01,  // Mode 18
  0x0f, 0x10, 0x0e, 0x0f, 0x0d, 0x0e, 0x0c, 0x0d, 0x0b, 0x0c, 0x0a, 0x0b, 0x09, 0x0a, 0x08, 0x09, 0x07, 0x08, 0x06, 0x07, 0x05, 0x06, 0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01,
};

static ALIGNED(32) const int8_t intra_chroma_linear_interpolation_shuffle_vectors_w4_ver[] = {
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 2
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,
  0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 3
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,
  0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 4
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 5
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 6
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 7
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 8
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 9
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 10
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 11
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 12
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,  // Mode 13
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,  // Mode 14
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,  // Mode 15
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,  // Mode 16
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,  // Mode 17
  0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
  0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,  // Mode 18
  0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
};

static ALIGNED(32) const int8_t intra_chroma_linear_interpolation_weights_w4_ver[] = {
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,  // Mode 2 
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,
   6, 26,  6, 26,  6, 26,  6, 26, 12, 20, 12, 20, 12, 20, 12, 20,  // Mode 3 
  18, 14, 18, 14, 18, 14, 18, 14, 24,  8, 24,  8, 24,  8, 24,  8,
  11, 21, 11, 21, 11, 21, 11, 21, 22, 10, 22, 10, 22, 10, 22, 10,  // Mode 4 
   1, 31,  1, 31,  1, 31,  1, 31, 12, 20, 12, 20, 12, 20, 12, 20,
  15, 17, 15, 17, 15, 17, 15, 17, 30,  2, 30,  2, 30,  2, 30,  2,  // Mode 5 
  13, 19, 13, 19, 13, 19, 13, 19, 28,  4, 28,  4, 28,  4, 28,  4,
  19, 13, 19, 13, 19, 13, 19, 13,  6, 26,  6, 26,  6, 26,  6, 26,  // Mode 6 
  25,  7, 25,  7, 25,  7, 25,  7, 12, 20, 12, 20, 12, 20, 12, 20,
  23,  9, 23,  9, 23,  9, 23,  9, 14, 18, 14, 18, 14, 18, 14, 18,  // Mode 7 
   5, 27,  5, 27,  5, 27,  5, 27, 28,  4, 28,  4, 28,  4, 28,  4,
  27,  5, 27,  5, 27,  5, 27,  5, 22, 10, 22, 10, 22, 10, 22, 10,  // Mode 8 
  17, 15, 17, 15, 17, 15, 17, 15, 12, 20, 12, 20, 12, 20, 12, 20,
  30,  2, 30,  2, 30,  2, 30,  2, 28,  4, 28,  4, 28,  4, 28,  4,  // Mode 9 
  26,  6, 26,  6, 26,  6, 26,  6, 24,  8, 24,  8, 24,  8, 24,  8,
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,  // Mode 10 
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,
   2, 30,  2, 30,  2, 30,  2, 30,  4, 28,  4, 28,  4, 28,  4, 28,  // Mode 11 
   6, 26,  6, 26,  6, 26,  6, 26,  8, 24,  8, 24,  8, 24,  8, 24,
   5, 27,  5, 27,  5, 27,  5, 27, 10, 22, 10, 22, 10, 22, 10, 22,  // Mode 12 
  15, 17, 15, 17, 15, 17, 15, 17, 20, 12, 20, 12, 20, 12, 20, 12,
   9, 23,  9, 23,  9, 23,  9, 23, 18, 14, 18, 14, 18, 14, 18, 14,  // Mode 13 
  27,  5, 27,  5, 27,  5, 27,  5,  4, 28,  4, 28,  4, 28,  4, 28,
  13, 19, 13, 19, 13, 19, 13, 19, 26,  6, 26,  6, 26,  6, 26,  6,  // Mode 14 
   7, 25,  7, 25,  7, 25,  7, 25, 20, 12, 20, 12, 20, 12, 20, 12,
  17, 15, 17, 15, 17, 15, 17, 15,  2, 30,  2, 30,  2, 30,  2, 30,  // Mode 15 
  19, 13, 19, 13, 19, 13, 19, 13,  4, 28,  4, 28,  4, 28,  4, 28,
  21, 11, 21, 11, 21, 11, 21, 11, 10, 22, 10, 22, 10, 22, 10, 22,  // Mode 16 
  31,  1, 31,  1, 31,  1, 31,  1, 20, 12, 20, 12, 20, 12, 20, 12,
  26,  6, 26,  6, 26,  6, 26,  6, 20, 12, 20, 12, 20, 12, 20, 12,  // Mode 17 
  14, 18, 14, 18, 14, 18, 14, 18,  8, 24,  8, 24,  8, 24,  8, 24,
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,  // Mode 18 
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,
};

static ALIGNED(32) const int8_t intra_chroma_linear_interpolation_weights_w4_hor[] = {
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 2
   6, 26, 12, 20, 18, 14, 24,  8,  6, 26, 12, 20, 18, 14, 24,  8,   // Mode 3
  11, 21, 22, 10,  1, 31, 12, 20, 11, 21, 22, 10,  1, 31, 12, 20,   // Mode 4
  15, 17, 30,  2, 13, 19, 28,  4, 15, 17, 30,  2, 13, 19, 28,  4,   // Mode 5
  19, 13,  6, 26, 25,  7, 12, 20, 19, 13,  6, 26, 25,  7, 12, 20,   // Mode 6
  23,  9, 14, 18,  5, 27, 28,  4, 23,  9, 14, 18,  5, 27, 28,  4,   // Mode 7
  27,  5, 22, 10, 17, 15, 12, 20, 27,  5, 22, 10, 17, 15, 12, 20,   // Mode 8
  30,  2, 28,  4, 26,  6, 24,  8, 30,  2, 28,  4, 26,  6, 24,  8,   // Mode 9
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 10
   2, 30,  4, 28,  6, 26,  8, 24,  2, 30,  4, 28,  6, 26,  8, 24,   // Mode 11
   5, 27, 10, 22, 15, 17, 20, 12,  5, 27, 10, 22, 15, 17, 20, 12,   // Mode 12
   9, 23, 18, 14, 27,  5,  4, 28,  9, 23, 18, 14, 27,  5,  4, 28,   // Mode 13
  13, 19, 26,  6,  7, 25, 20, 12, 13, 19, 26,  6,  7, 25, 20, 12,   // Mode 14
  17, 15,  2, 30, 19, 13,  4, 28, 17, 15,  2, 30, 19, 13,  4, 28,   // Mode 15
  21, 11, 10, 22, 31,  1, 20, 12, 21, 11, 10, 22, 31,  1, 20, 12,   // Mode 16
  26,  6, 20, 12, 14, 18,  8, 24, 26,  6, 20, 12, 14, 18,  8, 24,   // Mode 17
  32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0, 32,  0,   // Mode 18
};

static ALIGNED(32) const int8_t intra_chroma_linear_interpolation_shuffle_vectors_w4_hor[] = {
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 2
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c,
  0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d,
  0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,  // Mode 3
  0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05,
  0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c,
  0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d,
  0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03,  // Mode 4
  0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04,
  0x08, 0x09, 0x09, 0x0a, 0x09, 0x0a, 0x0a, 0x0b,
  0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0b, 0x0b, 0x0c,
  0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03,  // Mode 5
  0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04,
  0x08, 0x09, 0x09, 0x0a, 0x09, 0x0a, 0x0a, 0x0b,
  0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0b, 0x0b, 0x0c,
  0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02,  // Mode 6
  0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03,
  0x08, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x09, 0x0a,
  0x09, 0x0a, 0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0b,
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02,  // Mode 7
  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03,
  0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x09, 0x0a,
  0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a, 0x0a, 0x0b,
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,  // Mode 8
  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
  0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09,
  0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a,
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,  // Mode 9
  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
  0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09,
  0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a,
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,  // Mode 10
  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
  0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09,
  0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a,
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,  // Mode 11
  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
  0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09,
  0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a,
  0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,  // Mode 12
  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
  0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09,
  0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a,
  0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,  // Mode 13
  0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02,
  0x09, 0x0a, 0x09, 0x0a, 0x09, 0x0a, 0x08, 0x09,
  0x0a, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x09, 0x0a,
  0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01,  // Mode 14
  0x02, 0x03, 0x02, 0x03, 0x01, 0x02, 0x01, 0x02,
  0x09, 0x0a, 0x09, 0x0a, 0x08, 0x09, 0x08, 0x09,
  0x0a, 0x0b, 0x0a, 0x0b, 0x09, 0x0a, 0x09, 0x0a,
  0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,  // Mode 15
  0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02,
  0x0a, 0x0b, 0x09, 0x0a, 0x09, 0x0a, 0x08, 0x09,
  0x0b, 0x0c, 0x0a, 0x0b, 0x0a, 0x0b, 0x09, 0x0a,
  0x02, 0x03, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,  // Mode 16
  0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x01, 0x02,
  0x0a, 0x0b, 0x09, 0x0a, 0x09, 0x0a, 0x08, 0x09,
  0x0b, 0x0c, 0x0a, 0x0b, 0x0a, 0x0b, 0x09, 0x0a,
  0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01,  // Mode 17
  0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02,
  0x0b, 0x0c, 0x0a, 0x0b, 0x09, 0x0a, 0x08, 0x09,
  0x0c, 0x0d, 0x0b, 0x0c, 0x0a, 0x0b, 0x09, 0x0a,
  0x03, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01,  // Mode 18
  0x04, 0x05, 0x03, 0x04, 0x02, 0x03, 0x01, 0x02,
  0x0b, 0x0c, 0x0a, 0x0b, 0x09, 0x0a, 0x08, 0x09,
  0x0c, 0x0d, 0x0b, 0x0c, 0x0a, 0x0b, 0x09, 0x0a,
};


//  Linear interpolation filter for width 4 has a different call, since it uses premade tables for coefficients
static INLINE void angular_pred_avx2_linear_filter_w4_ver(kvz_pixel* dst, const kvz_pixel* const ref, const int16_t* delta_int, const int32_t pred_mode)
{
  const int16_t* dint = delta_int;
  const __m128i v16s = _mm_set1_epi16(16);

  const int mode_idx = (pred_mode <= 18 ? (pred_mode - 2) : (34 - pred_mode));
  const int table_offset = mode_idx * 32;

  // Load refs from smallest index onwards, shuffle will handle the rest. The smallest index will be at one of these delta int table indices
  const int16_t min_offset =  MIN(dint[0], dint[3]);
  dint += 4;
  // Load enough reff samples to cover four 4 width lines. Shuffles will put the samples in correct places.
  const __m128i vsrc_raw = _mm_loadu_si128((const __m128i*) & ref[min_offset]);

  const __m128i vcoeff0 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_weights_w4_ver[table_offset]);
  const __m128i vcoeff1 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_weights_w4_ver[table_offset + 16]);

  const __m128i vshuf0 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w4_ver[table_offset + 0]);
  const __m128i vshuf1 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w4_ver[table_offset + 16]);

  __m128i vsrc0 = _mm_shuffle_epi8(vsrc_raw, vshuf0);
  __m128i vsrc1 = _mm_shuffle_epi8(vsrc_raw, vshuf1);

  __m128i res0 = _mm_maddubs_epi16(vsrc0, vcoeff0);
  __m128i res1 = _mm_maddubs_epi16(vsrc1, vcoeff1);
  res0 = _mm_add_epi16(res0, v16s);
  res1 = _mm_add_epi16(res1, v16s);
  res0 = _mm_srai_epi16(res0, 5);
  res1 = _mm_srai_epi16(res1, 5);

  _mm_store_si128((__m128i*)dst, _mm_packus_epi16(res0, res1));  
}


static INLINE void angular_pred_avx2_linear_filter_w8_ver(kvz_pixel* dst, const kvz_pixel* const ref, const int16_t* delta_int, const int pred_mode)
{
  const int height = 8;
  const __m128i v16s = _mm_set1_epi16(16);
  const __m128i vshuf = _mm_setr_epi8(
    0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
    0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08
  );

  const int mode_idx = (pred_mode <= 18 ? (pred_mode - 2) : (34 - pred_mode));
  const int coeff_table_offset = mode_idx * 64;

  // Height has to be at least 2, handle 2 lines at once
  for (int y = 0; y < height; y += 2) {
    const int16_t* coeff_tmp0 = (const int16_t*)&intra_chroma_linear_interpolation_weights_w8_ver[coeff_table_offset + (y << 1) + 0];
    const int16_t* coeff_tmp1 = (const int16_t*)&intra_chroma_linear_interpolation_weights_w8_ver[coeff_table_offset + (y << 1) + 2];

    __m128i vsrc0 = _mm_loadu_si128((const __m128i*) & ref[delta_int[y + 0]]);
    __m128i vsrc1 = _mm_loadu_si128((const __m128i*) & ref[delta_int[y + 1]]);

    vsrc0 = _mm_shuffle_epi8(vsrc0, vshuf);
    vsrc1 = _mm_shuffle_epi8(vsrc1, vshuf);

    const __m128i vcoeff0 = _mm_set1_epi16(*coeff_tmp0);
    const __m128i vcoeff1 = _mm_set1_epi16(*coeff_tmp1);

    __m128i res0 = _mm_maddubs_epi16(vsrc0, vcoeff0);
    __m128i res1 = _mm_maddubs_epi16(vsrc1, vcoeff1);
    res0 = _mm_add_epi16(res0, v16s);
    res1 = _mm_add_epi16(res1, v16s);
    res0 = _mm_srai_epi16(res0, 5);
    res1 = _mm_srai_epi16(res1, 5);

    _mm_store_si128((__m128i*)dst, _mm_packus_epi16(res0, res1));
    dst += 16;
  }
}


static INLINE void angular_pred_avx2_linear_filter_w16_ver(kvz_pixel* dst, const kvz_pixel* const ref, const int16_t* delta_int, const int pred_mode)
{
  const int height = 16;
  const __m128i v16s = _mm_set1_epi16(16);
  const __m128i vshuf = _mm_setr_epi8(
    0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
    0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08
  );

  const int mode_idx = (pred_mode <= 18 ? (pred_mode - 2) : (34 - pred_mode));
  const int coeff_table_offset = mode_idx * 64;

  // Handle 1 line at a time
  for (int y = 0; y < height; ++y) {
    const int16_t* coeff_tmp = (const int16_t*)&intra_chroma_linear_interpolation_weights_w8_ver[coeff_table_offset + (y << 1)];
    __m128i vcoeff = _mm_set1_epi16(*coeff_tmp);

    __m128i vsrc0 = _mm_loadu_si128((const __m128i*) & ref[delta_int[y] + 0]);
    __m128i vsrc1 = _mm_loadu_si128((const __m128i*) & ref[delta_int[y] + 8]);

    vsrc0 = _mm_shuffle_epi8(vsrc0, vshuf);
    vsrc1 = _mm_shuffle_epi8(vsrc1, vshuf);

    __m128i res0 = _mm_maddubs_epi16(vsrc0, vcoeff);
    __m128i res1 = _mm_maddubs_epi16(vsrc1, vcoeff);
    res0 = _mm_add_epi16(res0, v16s);
    res1 = _mm_add_epi16(res1, v16s);
    res0 = _mm_srai_epi16(res0, 5);
    res1 = _mm_srai_epi16(res1, 5);

    _mm_store_si128((__m128i*)dst, _mm_packus_epi16(res0, res1));
    dst += 16;
  }
}

NO_ASAN
static INLINE void angular_pred_avx2_linear_filter_w32_ver(kvz_pixel* dst, const kvz_pixel* const ref, const int16_t* delta_int, const int pred_mode)
{
  const int height = 32;
  const __m256i v16s = _mm256_set1_epi16(16);
  const __m256i vshuf = _mm256_setr_epi8(
    0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
    0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08,
    0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
    0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08
  );

  const int mode_idx = (pred_mode <= 18 ? (pred_mode - 2) : (34 - pred_mode));
  const int coeff_table_offset = mode_idx * 64;

  // Handle 1 line at a time
  for (int y = 0; y < height; ++y) {
    const int16_t* coeff_tmp = (const int16_t*)&intra_chroma_linear_interpolation_weights_w8_ver[coeff_table_offset + (y << 1)];
    __m256i vcoeff = _mm256_set1_epi16(*coeff_tmp);

    ALIGNED(32) __m128i vsrc[4];
    vsrc[0] = _mm_loadu_si128((const __m128i*) & ref[delta_int[y] + 0 ]);
    vsrc[1] = _mm_loadu_si128((const __m128i*) & ref[delta_int[y] + 16]); // Flip these two middle sources. They will be later flipped back into place by packus
    vsrc[2] = _mm_loadu_si128((const __m128i*) & ref[delta_int[y] + 8 ]);
    vsrc[3] = _mm_loadu_si128((const __m128i*) & ref[delta_int[y] + 24]);

    __m256i* vsrc256 = (__m256i*)vsrc;
    vsrc256[0] = _mm256_shuffle_epi8(vsrc256[0], vshuf);
    vsrc256[1] = _mm256_shuffle_epi8(vsrc256[1], vshuf);

    __m256i res0 = _mm256_maddubs_epi16(vsrc256[0], vcoeff);
    __m256i res1 = _mm256_maddubs_epi16(vsrc256[1], vcoeff);
    res0 = _mm256_add_epi16(res0, v16s);
    res1 = _mm256_add_epi16(res1, v16s);
    res0 = _mm256_srai_epi16(res0, 5);
    res1 = _mm256_srai_epi16(res1, 5);

    _mm256_store_si256((__m256i*)dst, _mm256_packus_epi16(res0, res1));
    dst += 32;
  }
}


static INLINE void angular_pred_avx2_linear_filter_w4_hor(kvz_pixel* dst, const kvz_pixel* const ref, const int mode, const int16_t* delta_int)
{
  const int16_t* dint = delta_int;
  const __m128i v16s = _mm_set1_epi16(16);

  const int16_t weigth_offset = (mode - 2) * 16;
  const int16_t shuf_offset = (mode - 2) * 32;

  __m128i vcoeff = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_weights_w4_hor[weigth_offset]);
  __m128i vshuf0 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w4_hor[shuf_offset + 0]);
  __m128i vshuf1 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w4_hor[shuf_offset + 16]);

  // Load refs from smallest index onwards, shuffle will handle the rest. The smallest index will be at one of these delta int table indices
  const int16_t min_offset = MIN(dint[0], dint[3]);


  // Prepare sources
  __m128i vidx = _mm_set_epi64x((long long int)(min_offset + 2), (long long int)(min_offset + 0));
  __m128i vsrc_tmp = _mm_i64gather_epi64((const long long*)ref, vidx, 1);
  __m128i vsrc0 = _mm_shuffle_epi8(vsrc_tmp, vshuf0);
  __m128i vsrc1 = _mm_shuffle_epi8(vsrc_tmp, vshuf1);

  __m128i res0 = _mm_maddubs_epi16(vsrc0, vcoeff);
  __m128i res1 = _mm_maddubs_epi16(vsrc1, vcoeff);
  res0 = _mm_add_epi16(res0, v16s);
  res1 = _mm_add_epi16(res1, v16s);
  res0 = _mm_srai_epi16(res0, 5);
  res1 = _mm_srai_epi16(res1, 5);

  _mm_store_si128((__m128i*)dst, _mm_packus_epi16(res0, res1));
  dst += 16;
  
}


static INLINE void angular_pred_avx2_linear_filter_w8_hor(kvz_pixel* dst, const kvz_pixel* const ref, const int mode, const int16_t* delta_int)
{
  const int height = 8;
  const int16_t* dint = delta_int;
  const __m128i v16s = _mm_set1_epi16(16);
  const int16_t weigth_offset = (mode - 2) * 16;
  const int16_t shuf_offset = (mode - 2) * 32;

  __m128i vcoeff = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_weights_w8_hor[weigth_offset]);
  __m128i vshuf0 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w8_hor[shuf_offset + 0]);
  __m128i vshuf1 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w8_hor[shuf_offset + 16]);

  // Load refs from smallest index onwards, shuffle will handle the rest. The smallest index will be at one of these delta int table indices
  const int16_t min_offset = MIN(dint[0], dint[7]);

  // Height has to be at least 2, handle 2 lines at once
  for (int y = 0; y < height; y += 2) {
    // Prepare sources
    __m128i vsrc_tmp = _mm_loadu_si128((__m128i*) & ref[min_offset + y]);
    const __m128i vsrc0 = _mm_shuffle_epi8(vsrc_tmp, vshuf0);
    const __m128i vsrc1 = _mm_shuffle_epi8(vsrc_tmp, vshuf1);

    __m128i res0 = _mm_maddubs_epi16(vsrc0, vcoeff);
    __m128i res1 = _mm_maddubs_epi16(vsrc1, vcoeff);
    res0 = _mm_add_epi16(res0, v16s);
    res1 = _mm_add_epi16(res1, v16s);
    res0 = _mm_srai_epi16(res0, 5);
    res1 = _mm_srai_epi16(res1, 5);

    _mm_store_si128((__m128i*)dst, _mm_packus_epi16(res0, res1));
    dst += 16;
  }
}


static INLINE void angular_pred_avx2_linear_filter_w16_hor(kvz_pixel* dst, const kvz_pixel* const ref, const int mode, const int16_t* delta_int)
{
  const int height = 16;
  const int16_t* dint = delta_int;
  const __m128i v16s = _mm_set1_epi16(16);
  const int16_t weigth_offset = (mode - 2) * 32;
  const int16_t shuf_offset = (mode - 2) * 64;

  __m128i vcoeff0 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_weights_w16_hor[weigth_offset + 0]);
  __m128i vcoeff1 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_weights_w16_hor[weigth_offset + 16]);
  __m128i vshuf0 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w16_hor[shuf_offset + 0]);
  __m128i vshuf1 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w16_hor[shuf_offset + 16]);
  __m128i vshuf2 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w16_hor[shuf_offset + 32]);
  __m128i vshuf3 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w16_hor[shuf_offset + 48]);

  // Load refs from smallest index onwards, shuffle will handle the rest. The smallest index will be at one of these delta int table indices
  const int16_t min_offset0 = MIN(dint[0], dint[7]);
  const int16_t min_offset1 = MIN(dint[8], dint[15]);

  // Height has to be at least 2, there is no 16x1 block for chroma.
  for (int y = 0; y < height; y += 2) {
    // Prepare sources
    __m128i vsrc_tmp0 = _mm_loadu_si128((__m128i*) & ref[min_offset0 + y]);
    __m128i vsrc_tmp1 = _mm_loadu_si128((__m128i*) & ref[min_offset1 + y]);
    const __m128i vsrc0 = _mm_shuffle_epi8(vsrc_tmp0, vshuf0);
    const __m128i vsrc1 = _mm_shuffle_epi8(vsrc_tmp1, vshuf1);
    const __m128i vsrc2 = _mm_shuffle_epi8(vsrc_tmp0, vshuf2);
    const __m128i vsrc3 = _mm_shuffle_epi8(vsrc_tmp1, vshuf3);

    __m128i res0 = _mm_maddubs_epi16(vsrc0, vcoeff0);
    __m128i res1 = _mm_maddubs_epi16(vsrc1, vcoeff1);
    __m128i res2 = _mm_maddubs_epi16(vsrc2, vcoeff0);
    __m128i res3 = _mm_maddubs_epi16(vsrc3, vcoeff1);
    res0 = _mm_add_epi16(res0, v16s);
    res1 = _mm_add_epi16(res1, v16s);
    res2 = _mm_add_epi16(res2, v16s);
    res3 = _mm_add_epi16(res3, v16s);
    res0 = _mm_srai_epi16(res0, 5);
    res1 = _mm_srai_epi16(res1, 5);
    res2 = _mm_srai_epi16(res2, 5);
    res3 = _mm_srai_epi16(res3, 5);

    _mm_store_si128((__m128i*) & dst[0], _mm_packus_epi16(res0, res1));
    _mm_store_si128((__m128i*) & dst[16], _mm_packus_epi16(res2, res3));
    dst += 32;
  }
}


NO_ASAN
static INLINE void angular_pred_avx2_linear_filter_w32_hor(kvz_pixel* dst, const kvz_pixel* const ref, const int mode, const int16_t* delta_int)
{
  const int height = 32;
  const int16_t* dint = delta_int;
  const __m128i v16s = _mm_set1_epi16(16);
  const int16_t weigth_offset = (mode - 2) * 64;
  const int16_t shuf_offset = (mode - 2) * 64;

  __m128i vcoeff0 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_weights_w32_hor[weigth_offset + 0]);
  __m128i vcoeff1 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_weights_w32_hor[weigth_offset + 16]);
  __m128i vcoeff2 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_weights_w32_hor[weigth_offset + 32]);
  __m128i vcoeff3 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_weights_w32_hor[weigth_offset + 48]);
  __m128i vshuf0 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w32_hor[shuf_offset + 0]);
  __m128i vshuf1 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w32_hor[shuf_offset + 16]);
  __m128i vshuf2 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w32_hor[shuf_offset + 32]);
  __m128i vshuf3 = _mm_load_si128((const __m128i*) & intra_chroma_linear_interpolation_shuffle_vectors_w32_hor[shuf_offset + 48]);

  // Load refs from smallest index onwards, shuffle will handle the rest. The smallest index will be at one of these delta int table indices
  // Due to width, two loads are needed, and therefore two offsets. Cannot use 256-bit loads due to alignment issues.
  const int16_t min_offset0 = MIN(dint[0], dint[15]);
  const int16_t min_offset1 = MIN(dint[16], dint[31]);

  // Height has to be at least 2. Due to width, handle 1 line at a time
  for (int y = 0; y < height; ++y) {
    // Prepare sources
    __m128i vsrc_tmp0 = _mm_loadu_si128((__m128i*) & ref[min_offset0 + y]);
    __m128i vsrc_tmp1 = _mm_loadu_si128((__m128i*) & ref[min_offset1 + y]);
    __m128i vsrc0 = _mm_shuffle_epi8(vsrc_tmp0, vshuf0);
    __m128i vsrc1 = _mm_shuffle_epi8(vsrc_tmp0, vshuf1);
    __m128i vsrc2 = _mm_shuffle_epi8(vsrc_tmp1, vshuf2);
    __m128i vsrc3 = _mm_shuffle_epi8(vsrc_tmp1, vshuf3);

    __m128i res0 = _mm_maddubs_epi16(vsrc0, vcoeff0);
    __m128i res1 = _mm_maddubs_epi16(vsrc1, vcoeff1);
    __m128i res2 = _mm_maddubs_epi16(vsrc2, vcoeff2);
    __m128i res3 = _mm_maddubs_epi16(vsrc3, vcoeff3);
    res0 = _mm_add_epi16(res0, v16s);
    res1 = _mm_add_epi16(res1, v16s);
    res2 = _mm_add_epi16(res2, v16s);
    res3 = _mm_add_epi16(res3, v16s);
    res0 = _mm_srai_epi16(res0, 5);
    res1 = _mm_srai_epi16(res1, 5);
    res2 = _mm_srai_epi16(res2, 5);
    res3 = _mm_srai_epi16(res3, 5);

    _mm_store_si128((__m128i*) & dst[0], _mm_packus_epi16(res0, res1));
    _mm_store_si128((__m128i*) & dst[16], _mm_packus_epi16(res2, res3));
    dst += 32;
  }
}

 /**
 * \brief Generage angular predictions.
 * \param log2_width    Log2 of width, range 2..5.
 * \param intra_mode    Angular mode in range 2..34.
 * \param in_ref_above  Pointer to -1 index of above reference, length=width*2+1.
 * \param in_ref_left   Pointer to -1 index of left reference, length=width*2+1.
 * \param dst           Buffer of size width*width.
 */
static void kvz_angular_pred_avx2(
  const int_fast8_t log2_width,
  const int_fast8_t intra_mode,
  const uint8_t *const in_ref_above,
  const uint8_t *const in_ref_left,
  uint8_t *const dst)
{
  assert(log2_width >= 2 && log2_width <= 5);
  assert(intra_mode >= 2 && intra_mode <= 34);

  static const int8_t modedisp2sampledisp[9] = { 0, 2, 5, 9, 13, 17, 21, 26, 32 };
  static const int16_t modedisp2invsampledisp[9] = { 0, 4096, 1638, 910, 630, 482, 390, 315, 256 }; // (256 * 32) / sampledisp

                                                    // Temporary buffer for modes 11-25.
                                                    // It only needs to be big enough to hold indices from -width to width-1.
  uint8_t tmp_ref[2 * 32];
  const int_fast8_t width = 1 << log2_width;

  // Whether to swap references to always project on the left reference row.
  const bool vertical_mode = intra_mode >= 18;
  // Modes distance to horizontal or vertical mode.
  const int_fast8_t mode_disp = vertical_mode ? intra_mode - 26 : 10 - intra_mode;
  // Sample displacement per column in fractions of 32.
  const int_fast8_t sample_disp = (mode_disp < 0 ? -1 : 1) * modedisp2sampledisp[abs(mode_disp)];

  // Pointer for the reference we are interpolating from.
  const uint8_t *ref_main;
  // Pointer for the other reference.
  const uint8_t *ref_side;

  // Set ref_main and ref_side such that, when indexed with 0, they point to
  // index 0 in block coordinates.
  if (sample_disp < 0) {
    // Negative sample_disp means, we need to use both references.

    ref_side = (vertical_mode ? in_ref_left : in_ref_above) + 1;
    ref_main = (vertical_mode ? in_ref_above : in_ref_left) + 1;

    // Move the reference pixels to start from the middle to the later half of
    // the tmp_ref, so there is room for negative indices.
    for (int_fast8_t x = -1; x < width; ++x) {
      tmp_ref[x + width] = ref_main[x];
    }
    // Get a pointer to block index 0 in tmp_ref.
    ref_main = tmp_ref + width;

    // Extend the side reference to the negative indices of main reference.
    int_fast32_t col_sample_disp = 128; // rounding for the ">> 8"
    int_fast16_t inv_abs_sample_disp = modedisp2invsampledisp[abs(mode_disp)];
    int_fast8_t most_negative_index = (width * sample_disp) >> 5;
    for (int_fast8_t x = -2; x >= most_negative_index; --x) {
      col_sample_disp += inv_abs_sample_disp;
      int_fast8_t side_index = col_sample_disp >> 8;
      tmp_ref[x + width] = ref_side[side_index - 1];
    }
  }
  else {
    // sample_disp >= 0 means we don't need to refer to negative indices,
    // which means we can just use the references as is.
    ref_main = (vertical_mode ? in_ref_above : in_ref_left) + 1;
    ref_side = (vertical_mode ? in_ref_left : in_ref_above) + 1;
  }


  if(sample_disp == 0) {
    if (vertical_mode) {
      for (int_fast32_t y = 0; y < width; ++y) {
        switch (width) {
        case 4:  memcpy(&dst[y * 4], &ref_main[0], 4 * sizeof(uint8_t)); break;
        case 8:  memcpy(&dst[y * 8], &ref_main[0], 8 * sizeof(uint8_t)); break;
        case 16: memcpy(&dst[y * 16], &ref_main[0], 16 * sizeof(uint8_t)); break;
        case 32: memcpy(&dst[y * 32], &ref_main[0], 32 * sizeof(uint8_t)); break;
        default:
          assert(false && "Intra angular predicion: illegal width.\n");
          break;
        }
      }
    }
    else {
      for (int y = 0; y < width; ++y) {
        switch (width) {
        case 4:  memset(&dst[y * 4], ref_main[y], 4 * sizeof(uint8_t)); break;
        case 8:  memset(&dst[y * 8], ref_main[y], 8 * sizeof(uint8_t)); break;
        case 16: memset(&dst[y * 16], ref_main[y], 16 * sizeof(uint8_t)); break;
        case 32: memset(&dst[y * 32], ref_main[y], 32 * sizeof(uint8_t)); break;
        default:
          assert(false && "Intra angular predicion: illegal width.\n");
          break;
        }
      }
    }
    return;
  }

  if ((abs(sample_disp) & 0x1f )== 0) {
    int table_offset = vertical_mode ? (34 - intra_mode) * 32 : (intra_mode - 2) * 32;
    const int16_t* delta_int = &delta_int_table[table_offset];
    for (int y = 0; y < width; ++y) {
      uint8_t* dst_row = dst + y * width;
      const uint8_t* ref_row = ref_main + delta_int[y];
      switch (width) {
      case 4: memcpy(dst_row, ref_row, 4 * sizeof(uint8_t)); break;
      case 8: memcpy(dst_row, ref_row, 8 * sizeof(uint8_t)); break;
      case 16: memcpy(dst_row, ref_row, 16 * sizeof(uint8_t)); break;
      case 32: memcpy(dst_row, ref_row, 32 * sizeof(uint8_t)); break;
      }
    }
    return;
  }

  // The mode is not horizontal or vertical, we have to do interpolation.
  if (vertical_mode) {
    const int16_t* delta_int = &delta_int_table[(34 - intra_mode) * 32];
    switch (width) {
    case  4: angular_pred_avx2_linear_filter_w4_ver(dst, ref_main,  delta_int, intra_mode); break;
    case  8: angular_pred_avx2_linear_filter_w8_ver(dst, ref_main,  delta_int, intra_mode); break;
    case 16: angular_pred_avx2_linear_filter_w16_ver(dst, ref_main,  delta_int, intra_mode); break;
    case 32: angular_pred_avx2_linear_filter_w32_ver(dst, ref_main,  delta_int, intra_mode); break;
    default:
      break;
    }
  }
  else {
    const int16_t* delta_int = &delta_int_table[(intra_mode - 2) * 32];
    switch (width) {
    case  4: angular_pred_avx2_linear_filter_w4_hor(dst, ref_main, intra_mode, delta_int); break;
    case  8: angular_pred_avx2_linear_filter_w8_hor(dst, ref_main, intra_mode, delta_int); break;
    case 16: angular_pred_avx2_linear_filter_w16_hor(dst, ref_main, intra_mode, delta_int); break;
    case 32: angular_pred_avx2_linear_filter_w32_hor(dst, ref_main, intra_mode, delta_int); break;
    default:
      break;
    }
  }
}

/**
 * \brief Generate planar prediction.
 * \param log2_width    Log2 of width, range 2..5.
 * \param in_ref_above  Pointer to -1 index of above reference, length=width*2+1.
 * \param in_ref_left   Pointer to -1 index of left reference, length=width*2+1.
 * \param dst           Buffer of size width*width.
 */
static void kvz_intra_pred_planar_avx2(
  const int_fast8_t log2_width,
  const uint8_t *const ref_top,
  const uint8_t *const ref_left,
  uint8_t *const dst)
{
  assert(log2_width >= 2 && log2_width <= 5);

  const int_fast8_t width = 1 << log2_width;
  const uint8_t top_right = ref_top[width + 1];
  const uint8_t bottom_left = ref_left[width + 1];

  if (log2_width > 2) {
    
    __m128i v_width = _mm_set1_epi16(width);
    __m128i v_top_right = _mm_set1_epi16(top_right);
    __m128i v_bottom_left = _mm_set1_epi16(bottom_left);

    for (int y = 0; y < width; ++y) {

      __m128i x_plus_1 = _mm_setr_epi16(-7, -6, -5, -4, -3, -2, -1, 0);
      __m128i v_ref_left = _mm_set1_epi16(ref_left[y + 1]);
      __m128i y_plus_1 = _mm_set1_epi16(y + 1);

      for (int x = 0; x < width; x += 8) {
        x_plus_1 = _mm_add_epi16(x_plus_1, _mm_set1_epi16(8));
        __m128i v_ref_top = _mm_loadl_epi64((__m128i*)&(ref_top[x + 1]));
        v_ref_top = _mm_cvtepu8_epi16(v_ref_top);

        __m128i hor = _mm_add_epi16(_mm_mullo_epi16(_mm_sub_epi16(v_width, x_plus_1), v_ref_left), _mm_mullo_epi16(x_plus_1, v_top_right));
        __m128i ver = _mm_add_epi16(_mm_mullo_epi16(_mm_sub_epi16(v_width, y_plus_1), v_ref_top), _mm_mullo_epi16(y_plus_1, v_bottom_left));

        //dst[y * width + x] = ho

        __m128i chunk = _mm_srli_epi16(_mm_add_epi16(_mm_add_epi16(ver, hor), v_width), (log2_width + 1));
        chunk = _mm_packus_epi16(chunk, chunk);
        _mm_storel_epi64((__m128i*)&(dst[y * width + x]), chunk);
      }
    }
  } else {
    // Only if log2_width == 2 <=> width == 4
    assert(width == 4);
    const __m128i rl_shufmask = _mm_setr_epi32(0x04040404, 0x05050505,
                                               0x06060606, 0x07070707);

    const __m128i xp1   = _mm_set1_epi32  (0x04030201);
    const __m128i yp1   = _mm_shuffle_epi8(xp1,   rl_shufmask);

    const __m128i rdist = _mm_set1_epi32  (0x00010203);
    const __m128i bdist = _mm_shuffle_epi8(rdist, rl_shufmask);

    const __m128i wid16 = _mm_set1_epi16  (width);
    const __m128i tr    = _mm_set1_epi8   (top_right);
    const __m128i bl    = _mm_set1_epi8   (bottom_left);

    uint32_t rt14    = *(const uint32_t *)(ref_top  + 1);
    uint32_t rl14    = *(const uint32_t *)(ref_left + 1);
    uint64_t rt14_64 = (uint64_t)rt14;
    uint64_t rl14_64 = (uint64_t)rl14;
    uint64_t rtl14   = rt14_64 | (rl14_64 << 32);

    __m128i rtl_v    = _mm_cvtsi64_si128   (rtl14);
    __m128i rt       = _mm_broadcastd_epi32(rtl_v);
    __m128i rl       = _mm_shuffle_epi8    (rtl_v,    rl_shufmask);

    __m128i rtrl_l   = _mm_unpacklo_epi8   (rt,       rl);
    __m128i rtrl_h   = _mm_unpackhi_epi8   (rt,       rl);

    __m128i bdrd_l   = _mm_unpacklo_epi8   (bdist,    rdist);
    __m128i bdrd_h   = _mm_unpackhi_epi8   (bdist,    rdist);

    __m128i hvs_lo   = _mm_maddubs_epi16   (rtrl_l,   bdrd_l);
    __m128i hvs_hi   = _mm_maddubs_epi16   (rtrl_h,   bdrd_h);

    __m128i xp1yp1_l = _mm_unpacklo_epi8   (xp1,      yp1);
    __m128i xp1yp1_h = _mm_unpackhi_epi8   (xp1,      yp1);
    __m128i trbl_lh  = _mm_unpacklo_epi8   (tr,       bl);

    __m128i addend_l = _mm_maddubs_epi16   (trbl_lh,  xp1yp1_l);
    __m128i addend_h = _mm_maddubs_epi16   (trbl_lh,  xp1yp1_h);

            addend_l = _mm_add_epi16       (addend_l, wid16);
            addend_h = _mm_add_epi16       (addend_h, wid16);

    __m128i sum_l    = _mm_add_epi16       (hvs_lo,   addend_l);
    __m128i sum_h    = _mm_add_epi16       (hvs_hi,   addend_h);

    // Shift right by log2_width + 1
    __m128i sum_l_t  = _mm_srli_epi16      (sum_l,    3);
    __m128i sum_h_t  = _mm_srli_epi16      (sum_h,    3);
    __m128i result   = _mm_packus_epi16    (sum_l_t,  sum_h_t);
    _mm_storeu_si128((__m128i *)dst, result);
  }
}

// Calculate the DC value for a 4x4 block. The algorithm uses slightly
// different addends, multipliers etc for different pixels in the block,
// but for a fixed-size implementation one vector wide, all the weights,
// addends etc can be preinitialized for each position.
static void pred_filtered_dc_4x4(const uint8_t *ref_top,
                                 const uint8_t *ref_left,
                                       uint8_t *out_block)
{
  const uint32_t rt_u32 = *(const uint32_t *)(ref_top  + 1);
  const uint32_t rl_u32 = *(const uint32_t *)(ref_left + 1);

  const __m128i zero    = _mm_setzero_si128();
  const __m128i twos    = _mm_set1_epi8(2);

  // Hack. Move 4 u8's to bit positions 0, 64, 128 and 192 in two regs, to
  // expand them to 16 bits sort of "for free". Set highest bits on all the
  // other bytes in vectors to zero those bits in the result vector.
  const __m128i rl_shuf_lo = _mm_setr_epi32(0x80808000, 0x80808080,
                                            0x80808001, 0x80808080);
  const __m128i rl_shuf_hi = _mm_add_epi8  (rl_shuf_lo, twos);

  // Every second multiplier is 1, because we want maddubs to calculate
  // a + bc = 1 * a + bc (actually 2 + bc). We need to fill a vector with
  // ((u8)2)'s for other stuff anyway, so that can also be used here.
  const __m128i mult_lo = _mm_setr_epi32(0x01030102, 0x01030103,
                                         0x01040103, 0x01040104);
  const __m128i mult_hi = _mm_setr_epi32(0x01040103, 0x01040104,
                                         0x01040103, 0x01040104);
  __m128i four         = _mm_cvtsi32_si128  (4);
  __m128i rt           = _mm_cvtsi32_si128  (rt_u32);
  __m128i rl           = _mm_cvtsi32_si128  (rl_u32);
  __m128i rtrl         = _mm_unpacklo_epi32 (rt, rl);

  __m128i sad0         = _mm_sad_epu8       (rtrl, zero);
  __m128i sad1         = _mm_shuffle_epi32  (sad0, _MM_SHUFFLE(1, 0, 3, 2));
  __m128i sad2         = _mm_add_epi64      (sad0, sad1);
  __m128i sad3         = _mm_add_epi64      (sad2, four);

  __m128i dc_64        = _mm_srli_epi64     (sad3, 3);
  __m128i dc_8         = _mm_broadcastb_epi8(dc_64);

  __m128i rl_lo        = _mm_shuffle_epi8   (rl, rl_shuf_lo);
  __m128i rl_hi        = _mm_shuffle_epi8   (rl, rl_shuf_hi);

  __m128i rt_lo        = _mm_unpacklo_epi8  (rt, zero);
  __m128i rt_hi        = zero;

  __m128i dc_addend    = _mm_unpacklo_epi8(dc_8, twos);

  __m128i dc_multd_lo  = _mm_maddubs_epi16(dc_addend,    mult_lo);
  __m128i dc_multd_hi  = _mm_maddubs_epi16(dc_addend,    mult_hi);

  __m128i rl_rt_lo     = _mm_add_epi16    (rl_lo,        rt_lo);
  __m128i rl_rt_hi     = _mm_add_epi16    (rl_hi,        rt_hi);

  __m128i res_lo       = _mm_add_epi16    (dc_multd_lo,  rl_rt_lo);
  __m128i res_hi       = _mm_add_epi16    (dc_multd_hi,  rl_rt_hi);

          res_lo       = _mm_srli_epi16   (res_lo,       2);
          res_hi       = _mm_srli_epi16   (res_hi,       2);

  __m128i final        = _mm_packus_epi16 (res_lo,       res_hi);
  _mm_storeu_si128((__m128i *)out_block, final);
}

static void pred_filtered_dc_8x8(const uint8_t *ref_top,
                                 const uint8_t *ref_left,
                                       uint8_t *out_block)
{
  const uint64_t rt_u64 = *(const uint64_t *)(ref_top  + 1);
  const uint64_t rl_u64 = *(const uint64_t *)(ref_left + 1);

  const __m128i zero128 = _mm_setzero_si128();
  const __m256i twos    = _mm256_set1_epi8(2);

  // DC multiplier is 2 at (0, 0), 3 at (*, 0) and (0, *), and 4 at (*, *).
  // There is a constant addend of 2 on each pixel, use values from the twos
  // register and multipliers of 1 for that, to use maddubs for an (a*b)+c
  // operation.
  const __m256i mult_up_lo = _mm256_setr_epi32(0x01030102, 0x01030103,
                                               0x01030103, 0x01030103,
                                               0x01040103, 0x01040104,
                                               0x01040104, 0x01040104);

  // The 6 lowest rows have same multipliers, also the DC values and addends
  // are the same so this works for all of those
  const __m256i mult_rest  = _mm256_permute4x64_epi64(mult_up_lo, _MM_SHUFFLE(3, 2, 3, 2));

  // Every 8-pixel row starts with the next pixel of ref_left. Along with
  // doing the shuffling, also expand u8->u16, ie. move bytes 0 and 1 from
  // ref_left to bit positions 0 and 128 in rl_up_lo, 2 and 3 to rl_up_hi,
  // etc. The places to be zeroed out are 0x80 instead of the usual 0xff,
  // because this allows us to form new masks on the fly by adding 0x02-bytes
  // to this mask and still retain the highest bits as 1 where things should
  // be zeroed out.
  const __m256i rl_shuf_up_lo = _mm256_setr_epi32(0x80808000, 0x80808080,
                                                  0x80808080, 0x80808080,
                                                  0x80808001, 0x80808080,
                                                  0x80808080, 0x80808080);
  // And don't waste memory or architectural regs, hope these instructions
  // will be placed in between the shuffles by the compiler to only use one
  // register for the shufmasks, and executed way ahead of time because their
  // regs can be renamed.
  const __m256i rl_shuf_up_hi = _mm256_add_epi8 (rl_shuf_up_lo, twos);
  const __m256i rl_shuf_dn_lo = _mm256_add_epi8 (rl_shuf_up_hi, twos);
  const __m256i rl_shuf_dn_hi = _mm256_add_epi8 (rl_shuf_dn_lo, twos);

  __m128i eight         = _mm_cvtsi32_si128     (8);
  __m128i rt            = _mm_cvtsi64_si128     (rt_u64);
  __m128i rl            = _mm_cvtsi64_si128     (rl_u64);
  __m128i rtrl          = _mm_unpacklo_epi64    (rt, rl);

  __m128i sad0          = _mm_sad_epu8          (rtrl, zero128);
  __m128i sad1          = _mm_shuffle_epi32     (sad0, _MM_SHUFFLE(1, 0, 3, 2));
  __m128i sad2          = _mm_add_epi64         (sad0, sad1);
  __m128i sad3          = _mm_add_epi64         (sad2, eight);

  __m128i dc_64         = _mm_srli_epi64        (sad3, 4);
  __m256i dc_8          = _mm256_broadcastb_epi8(dc_64);

  __m256i dc_addend     = _mm256_unpacklo_epi8  (dc_8, twos);

  __m256i dc_up_lo      = _mm256_maddubs_epi16  (dc_addend, mult_up_lo);
  __m256i dc_rest       = _mm256_maddubs_epi16  (dc_addend, mult_rest);

  // rt_dn is all zeros, as is rt_up_hi. This'll get us the rl and rt parts
  // in A|B, C|D order instead of A|C, B|D that could be packed into abcd
  // order, so these need to be permuted before adding to the weighed DC
  // values.
  __m256i rt_up_lo      = _mm256_cvtepu8_epi16   (rt);

  __m256i rlrlrlrl      = _mm256_broadcastq_epi64(rl);
  __m256i rl_up_lo      = _mm256_shuffle_epi8    (rlrlrlrl, rl_shuf_up_lo);

  // Everything ref_top is zero except on the very first row
  __m256i rt_rl_up_hi   = _mm256_shuffle_epi8    (rlrlrlrl, rl_shuf_up_hi);
  __m256i rt_rl_dn_lo   = _mm256_shuffle_epi8    (rlrlrlrl, rl_shuf_dn_lo);
  __m256i rt_rl_dn_hi   = _mm256_shuffle_epi8    (rlrlrlrl, rl_shuf_dn_hi);

  __m256i rt_rl_up_lo   = _mm256_add_epi16       (rt_up_lo, rl_up_lo);

  __m256i rt_rl_up_lo_2 = _mm256_permute2x128_si256(rt_rl_up_lo, rt_rl_up_hi, 0x20);
  __m256i rt_rl_up_hi_2 = _mm256_permute2x128_si256(rt_rl_up_lo, rt_rl_up_hi, 0x31);
  __m256i rt_rl_dn_lo_2 = _mm256_permute2x128_si256(rt_rl_dn_lo, rt_rl_dn_hi, 0x20);
  __m256i rt_rl_dn_hi_2 = _mm256_permute2x128_si256(rt_rl_dn_lo, rt_rl_dn_hi, 0x31);

  __m256i up_lo = _mm256_add_epi16(rt_rl_up_lo_2, dc_up_lo);
  __m256i up_hi = _mm256_add_epi16(rt_rl_up_hi_2, dc_rest);
  __m256i dn_lo = _mm256_add_epi16(rt_rl_dn_lo_2, dc_rest);
  __m256i dn_hi = _mm256_add_epi16(rt_rl_dn_hi_2, dc_rest);

          up_lo = _mm256_srli_epi16(up_lo, 2);
          up_hi = _mm256_srli_epi16(up_hi, 2);
          dn_lo = _mm256_srli_epi16(dn_lo, 2);
          dn_hi = _mm256_srli_epi16(dn_hi, 2);

  __m256i res_up = _mm256_packus_epi16(up_lo, up_hi);
  __m256i res_dn = _mm256_packus_epi16(dn_lo, dn_hi);

  _mm256_storeu_si256(((__m256i *)out_block) + 0, res_up);
  _mm256_storeu_si256(((__m256i *)out_block) + 1, res_dn);
}

static INLINE __m256i cvt_u32_si256(const uint32_t u)
{
  const __m256i zero = _mm256_setzero_si256();
  return _mm256_insert_epi32(zero, u, 0);
}

static void pred_filtered_dc_16x16(const uint8_t *ref_top,
                                   const uint8_t *ref_left,
                                         uint8_t *out_block)
{
  const __m128i rt_128 = _mm_loadu_si128((const __m128i *)(ref_top  + 1));
  const __m128i rl_128 = _mm_loadu_si128((const __m128i *)(ref_left + 1));

  const __m128i zero_128 = _mm_setzero_si128();
  const __m256i zero     = _mm256_setzero_si256();
  const __m256i twos     = _mm256_set1_epi8(2);

  const __m256i mult_r0  = _mm256_setr_epi32(0x01030102, 0x01030103,
                                             0x01030103, 0x01030103,
                                             0x01030103, 0x01030103,
                                             0x01030103, 0x01030103);

  const __m256i mult_left = _mm256_set1_epi16(0x0103);

  // Leftmost bytes' blend mask, to move bytes (pixels) from the leftmost
  // column vector to the result row
  const __m256i lm8_bmask = _mm256_setr_epi32(0xff, 0, 0, 0, 0xff, 0, 0, 0);

  __m128i sixteen = _mm_cvtsi32_si128(16);
  __m128i sad0_t  = _mm_sad_epu8 (rt_128, zero_128);
  __m128i sad0_l  = _mm_sad_epu8 (rl_128, zero_128);
  __m128i sad0    = _mm_add_epi64(sad0_t, sad0_l);

  __m128i sad1    = _mm_shuffle_epi32      (sad0, _MM_SHUFFLE(1, 0, 3, 2));
  __m128i sad2    = _mm_add_epi64          (sad0, sad1);
  __m128i sad3    = _mm_add_epi64          (sad2, sixteen);

  __m128i dc_64   = _mm_srli_epi64         (sad3, 5);
  __m256i dc_8    = _mm256_broadcastb_epi8 (dc_64);

  __m256i rt      = _mm256_cvtepu8_epi16   (rt_128);
  __m256i rl      = _mm256_cvtepu8_epi16   (rl_128);

  uint8_t rl0       = *(uint8_t *)(ref_left + 1);
  __m256i rl_r0     = cvt_u32_si256((uint32_t)rl0);

  __m256i rlrt_r0   = _mm256_add_epi16(rl_r0, rt);

  __m256i dc_addend = _mm256_unpacklo_epi8(dc_8, twos);
  __m256i r0        = _mm256_maddubs_epi16(dc_addend, mult_r0);
  __m256i left_dcs  = _mm256_maddubs_epi16(dc_addend, mult_left);

          r0        = _mm256_add_epi16    (r0,       rlrt_r0);
          r0        = _mm256_srli_epi16   (r0, 2);
  __m256i r0r0      = _mm256_packus_epi16 (r0, r0);
          r0r0      = _mm256_permute4x64_epi64(r0r0, _MM_SHUFFLE(3, 1, 2, 0));

  __m256i leftmosts = _mm256_add_epi16    (left_dcs,  rl);
          leftmosts = _mm256_srli_epi16   (leftmosts, 2);

  // Contain the leftmost column's bytes in both lanes of lm_8
  __m256i lm_8      = _mm256_packus_epi16 (leftmosts, zero);
          lm_8      = _mm256_permute4x64_epi64(lm_8,  _MM_SHUFFLE(2, 0, 2, 0));

  __m256i lm8_r1    = _mm256_srli_epi32       (lm_8, 8);
  __m256i r1r1      = _mm256_blendv_epi8      (dc_8, lm8_r1, lm8_bmask);
  __m256i r0r1      = _mm256_blend_epi32      (r0r0, r1r1, 0xf0);

  _mm256_storeu_si256((__m256i *)out_block, r0r1);

  // Starts from 2 because row 0 (and row 1) is handled separately
  __m256i lm8_l     = _mm256_bsrli_epi128     (lm_8, 2);
  __m256i lm8_h     = _mm256_bsrli_epi128     (lm_8, 3);
          lm_8      = _mm256_blend_epi32      (lm8_l, lm8_h, 0xf0);

  for (uint32_t y = 2; y < 16; y += 2) {
    __m256i curr_row = _mm256_blendv_epi8 (dc_8, lm_8, lm8_bmask);
    _mm256_storeu_si256((__m256i *)(out_block + (y << 4)), curr_row);
    lm_8 = _mm256_bsrli_epi128(lm_8, 2);
  }
}

static void pred_filtered_dc_32x32(const uint8_t *ref_top,
                                   const uint8_t *ref_left,
                                         uint8_t *out_block)
{
  const __m256i rt = _mm256_loadu_si256((const __m256i *)(ref_top  + 1));
  const __m256i rl = _mm256_loadu_si256((const __m256i *)(ref_left + 1));

  const __m256i zero = _mm256_setzero_si256();
  const __m256i twos = _mm256_set1_epi8(2);

  const __m256i mult_r0lo = _mm256_setr_epi32(0x01030102, 0x01030103,
                                              0x01030103, 0x01030103,
                                              0x01030103, 0x01030103,
                                              0x01030103, 0x01030103);

  const __m256i mult_left = _mm256_set1_epi16(0x0103);
  const __m256i lm8_bmask = cvt_u32_si256    (0xff);

  const __m256i bshif_msk = _mm256_setr_epi32(0x04030201, 0x08070605,
                                              0x0c0b0a09, 0x800f0e0d,
                                              0x03020100, 0x07060504,
                                              0x0b0a0908, 0x0f0e0d0c);
  __m256i debias = cvt_u32_si256(32);
  __m256i sad0_t = _mm256_sad_epu8         (rt,     zero);
  __m256i sad0_l = _mm256_sad_epu8         (rl,     zero);
  __m256i sad0   = _mm256_add_epi64        (sad0_t, sad0_l);

  __m256i sad1   = _mm256_permute4x64_epi64(sad0,   _MM_SHUFFLE(1, 0, 3, 2));
  __m256i sad2   = _mm256_add_epi64        (sad0,   sad1);
  __m256i sad3   = _mm256_shuffle_epi32    (sad2,   _MM_SHUFFLE(1, 0, 3, 2));
  __m256i sad4   = _mm256_add_epi64        (sad2,   sad3);
  __m256i sad5   = _mm256_add_epi64        (sad4,   debias);
  __m256i dc_64  = _mm256_srli_epi64       (sad5,   6);

  __m128i dc_64_ = _mm256_castsi256_si128  (dc_64);
  __m256i dc_8   = _mm256_broadcastb_epi8  (dc_64_);

  __m256i rtlo   = _mm256_unpacklo_epi8    (rt, zero);
  __m256i rllo   = _mm256_unpacklo_epi8    (rl, zero);
  __m256i rthi   = _mm256_unpackhi_epi8    (rt, zero);
  __m256i rlhi   = _mm256_unpackhi_epi8    (rl, zero);

  __m256i dc_addend = _mm256_unpacklo_epi8 (dc_8, twos);
  __m256i r0lo   = _mm256_maddubs_epi16    (dc_addend, mult_r0lo);
  __m256i r0hi   = _mm256_maddubs_epi16    (dc_addend, mult_left);
  __m256i c0dc   = r0hi;

          r0lo   = _mm256_add_epi16        (r0lo, rtlo);
          r0hi   = _mm256_add_epi16        (r0hi, rthi);

  __m256i rlr0   = _mm256_blendv_epi8      (zero, rl, lm8_bmask);
          r0lo   = _mm256_add_epi16        (r0lo, rlr0);

          r0lo   = _mm256_srli_epi16       (r0lo, 2);
          r0hi   = _mm256_srli_epi16       (r0hi, 2);
  __m256i r0     = _mm256_packus_epi16     (r0lo, r0hi);

  _mm256_storeu_si256((__m256i *)out_block, r0);

  __m256i c0lo   = _mm256_add_epi16        (c0dc, rllo);
  __m256i c0hi   = _mm256_add_epi16        (c0dc, rlhi);
          c0lo   = _mm256_srli_epi16       (c0lo, 2);
          c0hi   = _mm256_srli_epi16       (c0hi, 2);

  __m256i c0     = _mm256_packus_epi16     (c0lo, c0hi);

  // r0 already handled!
  for (uint32_t y = 1; y < 32; y++) {
    if (y == 16) {
      c0 = _mm256_permute4x64_epi64(c0, _MM_SHUFFLE(1, 0, 3, 2));
    } else {
      c0 = _mm256_shuffle_epi8     (c0, bshif_msk);
    }
    __m256i curr_row = _mm256_blendv_epi8 (dc_8, c0, lm8_bmask);
    _mm256_storeu_si256(((__m256i *)out_block) + y, curr_row);
  }
}

/**
* \brief Generage intra DC prediction with post filtering applied.
* \param log2_width    Log2 of width, range 2..5.
* \param in_ref_above  Pointer to -1 index of above reference, length=width*2+1.
* \param in_ref_left   Pointer to -1 index of left reference, length=width*2+1.
* \param dst           Buffer of size width*width.
*/
static void kvz_intra_pred_filtered_dc_avx2(
  const int_fast8_t log2_width,
  const uint8_t *ref_top,
  const uint8_t *ref_left,
        uint8_t *out_block)
{
  assert(log2_width >= 2 && log2_width <= 5);

  if (log2_width == 2) {
    pred_filtered_dc_4x4(ref_top, ref_left, out_block);
  } else if (log2_width == 3) {
    pred_filtered_dc_8x8(ref_top, ref_left, out_block);
  } else if (log2_width == 4) {
    pred_filtered_dc_16x16(ref_top, ref_left, out_block);
  } else if (log2_width == 5) {
    pred_filtered_dc_32x32(ref_top, ref_left, out_block);
  }
}

#endif //KVZ_BIT_DEPTH == 8
#endif //COMPILE_INTEL_AVX2 && defined X86_64

int kvz_strategy_register_intra_avx2(void* opaque, uint8_t bitdepth)
{
  bool success = true;
#if COMPILE_INTEL_AVX2 && defined X86_64
#if KVZ_BIT_DEPTH == 8
  if (bitdepth == 8) {
    success &= kvz_strategyselector_register(opaque, "angular_pred", "avx2", 40, &kvz_angular_pred_avx2);
    success &= kvz_strategyselector_register(opaque, "intra_pred_planar", "avx2", 40, &kvz_intra_pred_planar_avx2);
    success &= kvz_strategyselector_register(opaque, "intra_pred_filtered_dc", "avx2", 40, &kvz_intra_pred_filtered_dc_avx2);
  }
#endif //KVZ_BIT_DEPTH == 8
#endif //COMPILE_INTEL_AVX2 && defined X86_64
  return success;
}

